/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.fresh.bus.canoe.xsocket;

import com.fresh.bus.canoe.message.MessageCodec;
import com.fresh.bus.canoe.message.Request;
import com.fresh.bus.canoe.server.LifecycleHandler;
import com.fresh.bus.canoe.session.Session;

import java.io.IOException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import org.xsocket.MaxReadSizeExceededException;
import org.xsocket.connection.IConnectHandler;
import org.xsocket.connection.IDataHandler;
import org.xsocket.connection.IDisconnectHandler;
import org.xsocket.connection.INonBlockingConnection;

/**
 *
 * @author panzd
 */
public class CanoeHandler implements IDataHandler, IConnectHandler, IDisconnectHandler {
    private LifecycleHandler lifecycleHandler;
    private MessageCodec messageCodec;
    private XSocketSessionFactory<XSocketSession> sessionFactory = new DefaultXSocketSessionFactory();

	public static final String SWF_AN_QUAN_REQUEST_BM = "UTF-8"; // 安全沙箱请求编码
	public static final String SWF_AN_QUAN_REQUEST = "<policy-file-request/>";// 安全沙箱请求字符串
	public String swf_AnQuan_Response = "<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"8090,8080,8020,8060\" /></cross-domain-policy>";// 安全沙箱应答字符串
    
    /**
     * @return the sessionFactory
     */
    public XSocketSessionFactory<XSocketSession> getSessionFactory() {
        return sessionFactory;
    }

    /**
     * @param sessionFactory the sessionFactory to set
     */
    public void setSessionFactory(XSocketSessionFactory<XSocketSession> sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    /**
     * @param lifecycleHandler the lifecycleHandler to set
     */
    public void setLifecycleHandler(LifecycleHandler lifecycleHandler) {
        this.lifecycleHandler = lifecycleHandler;
    }

    /**
     * @param messageCodec the messageCodec to set
     */
    protected void setMessageCodec(MessageCodec messageCodec) {
        this.messageCodec = messageCodec;
    }

    @Override
    public boolean onData(INonBlockingConnection conn) throws IOException,
            BufferUnderflowException, ClosedChannelException,
            MaxReadSizeExceededException {
    	
		if (conn.available() > 0) {
			if (conn.indexOf(SWF_AN_QUAN_REQUEST, SWF_AN_QUAN_REQUEST_BM) != -1) {
				ByteBuffer buffer = ByteBuffer.allocate(1024);
				conn.read(buffer);
				buffer.clear();
				conn.write(swf_AnQuan_Response + "\0");
				conn.flush();
				return true;
			}
		}    	
    	
        byte[] data = recieveData(conn);
    	//byte[] data = conn.readBytesByLength(conn.available());
        if (data == null) {
            return false;
        }

        Attachment attachment = (Attachment) conn.getAttachment();
        Request request = messageCodec.decodeRequest(data);
        lifecycleHandler.onRequest(attachment.getSession(), request);

        return false;
    }

    @Override
    public boolean onConnect(INonBlockingConnection conn) throws IOException,
            BufferUnderflowException, MaxReadSizeExceededException {
        if (conn.isAutoflush()) {
            conn.setAutoflush(false);
        }

        Session session = sessionFactory.create(conn, messageCodec);
        conn.setAttachment(new Attachment(session));

        lifecycleHandler.onConnected(session);

        return false;
    }

    @Override
    public boolean onDisconnect(INonBlockingConnection conn) throws IOException {
        Attachment attachment = (Attachment) conn.getAttachment();
        lifecycleHandler.onDisconnected(attachment.getSession());
        conn.setAttachment(null);

        return false;
    }

    private byte[] recieveData(INonBlockingConnection conn) throws IOException {
        Attachment attachment = (Attachment) conn.getAttachment();
        ByteBuffer dataBuffer = attachment.getBuffer();

        int length;
        byte[] data = null;

        if (dataBuffer == null) {
            length = conn.readShort();
            if (conn.available() < length) {
                dataBuffer = ByteBuffer.allocate(length);
                attachment.setBuffer(dataBuffer);
                conn.read(dataBuffer);
            } else {
                data = conn.readBytesByLength(length);
            }
        } else {
            conn.read(dataBuffer);

            if (dataBuffer.remaining() == 0) {
                if (dataBuffer.hasArray()) {
                    data = dataBuffer.array();
                } else {
                    dataBuffer.flip();

                    data = new byte[dataBuffer.limit()];
                    dataBuffer.get(data);
                }

                attachment.setBuffer(null);
            }
        }

        return data;
    }
}

class Attachment {
    private Session session;
    private ByteBuffer buffer;

    public Attachment(Session session) {
        this.session = session;
    }

    /**
     * @return the session
     */
    public Session getSession() {
        return session;
    }

    /**
     * @param session the session to set
     */
    public void setSession(Session session) {
        this.session = session;
    }

    /**
     * @return the buffer
     */
    public ByteBuffer getBuffer() {
        return buffer;
    }

    /**
     * @param buffer the buffer to set
     */
    public void setBuffer(ByteBuffer buffer) {
        this.buffer = buffer;
    }
}
